package demo.spring.framework.http.converter;

import java.nio.charset.StandardCharsets;
import java.util.List;

import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;

/**
 * Spring Boot底层通过HttpMessageConverters依靠Jackson库将Java实体类输出为JSON格式。当有多个转换器可用时，根据消息对象类型和需要的内容类型选择最适合的转换器使用。
 * <p>
 * <h1>HttpMessageConverter报文转换</h1>在SpringMVC中，可以使用@RequestBody和@ResponseBody两个注解，分别完成请求报文到对象和对象到响应报文的转换。
 * 底层这种灵活的消息转换机制，就是Spring3.x中新引入的HttpMessageConverter即消息转换器机制。
 * Http请求和响应报文本质上都是一串字符串，当请求报文来到java世界，它会被封装成为一个ServletInputStream的输入流，供我们读取报文。
 * 响应报文则是通过一个ServletOutputStream的输出流，来输出响应报文。我们从流中，只能读取到原始的字符串报文，同样，我们往输出流中，也只能写原始的字符。
 * 而在java世界中，处理业务逻辑，都是以一个个有业务意义的对象为处理维度的，那么在报文到达SpringMVC和从SpringMVC出去，都存在一个字符串到java对象的阻抗问题。
 * 在SpringMVC中，它是通过HttpMessageConverter机制处理。
 * <p>
 * <li>#HttpInputMessage这个类是SpringMVC内部对一次Http请求报文的抽象，在HttpMessageConverter的read()方法中，有一个HttpInputMessage的形参，
 * 它正是SpringMVC的消息转换器所作用的受体“请求消息”的内部抽象，消息转换器从“请求消息”中按照规则提取消息，转换为方法形参中声明的对象。
 * 
 * <li>#HttpOutputMessage这个类是SpringMVC内部对一次Http响应报文的抽象，在HttpMessageConverter的write()方法中，有一个HttpOutputMessage的形参，
 * 它正是SpringMVC的消息转换器所作用的受体“响应消息”的内部抽象，消息转换器将“响应消息”按照一定的规则写到响应报文中。
 * 
 * <li>#HttpMessageConverter对消息转换器最高层次的接口抽象，描述了一个消息转换器的一般特征，
 * HttpMessageConverter接口的定义出现了成对的canRead()，read()和canWrite()，write()方法，MediaType是对请求的Media Type属性的封装。
 * 在SpringMVC进入Controller方法前，会根据@RequestBody注解选择适当的HttpMessageConverter实现类来将请求参数解析到变量中，
 * canRead()方法返回true，然后它的read()方法会从请求中读出请求参数，绑定到Controller方法的变量中。
 * 当SpringMVC执行Controller方法后，由于返回值标识了@ResponseBody，SpringMVC将使用HttpMessageConverter的write()方法，将结果作为String值写入响应报文，
 * 当然，此时canWrite()方法返回true。
 * 
 * <h1>RequestResponseBodyMethodProcessor</h1>
 * 这个类同时实现了HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler两个接口。
 * 前者是将请求报文绑定到处理方法形参的策略接口，后者则是对处理方法返回值进行处理的策略接口。
 * 因为两个接口的实现，分别是以是否有@RequestBody和@ResponseBody为条件，然后分别调用HttpMessageConverter来进行消息的读写。
 * RequestResponseBodyMethodProcessor同时充当了方法参数解析和返回值处理两种角色。
 * <p>
 * 
 * 
 */
public class HttpMessageConverterDemo {

    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        HttpMessageConverter converter = new StringHttpMessageConverter();
        new StringHttpMessageConverter(StandardCharsets.UTF_8);
        new FormHttpMessageConverter();
        new MappingJackson2HttpMessageConverter();
        // 可以将Java对象转换为application/json
        new RequestResponseBodyMethodProcessor(converters);
        // 仅支持com.google.protobuf.Message类型的输入，但是可以输出application/json、application/xml、text/plain和application/x-protobuf这么多格式
        new ProtobufHttpMessageConverter();
        /*
         * 重写（override）configureMessageConverters方法，扩展现有的消息转换器链表。通过重写configureMessageConverters方法添加自定义的转换器很方便，但有一个弱点：
         * 如果项目中存在多个WebMvcConfigurers的实例（我们自己定义的，或者Spring Boot默认提供的），不能确保重写后的configureMessageConverters方法按照固定顺序执行。
         */
        // converters.add(new ByteArrayHttpMessageConverter());

    }
}
